Skip to content

fix(mcp): set_fields rejects reserved keys; add top-level description#158

Merged
avrabe merged 1 commit intomainfrom
fix/mcp-set-fields-scope
Apr 22, 2026
Merged

fix(mcp): set_fields rejects reserved keys; add top-level description#158
avrabe merged 1 commit intomainfrom
fix/mcp-set-fields-scope

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented Apr 21, 2026

Two related MCP bugs

Bug 1 — `yaml_edit.rs::modify_artifact_yaml` unconditionally nested every `set_fields` key under `fields:` using `format!("{key}: {value}")`. Passing `description=...` wrote `fields: {description: ...}` instead of touching top-level, and raw-interpolation corrupted YAML when the value contained backticks or newlines.

Bug 2 — MCP `ModifyParams` exposed only `status`, `title`, `add_tags`, `remove_tags`, `set_fields`. Top-level `description` (and `source-file`, `provenance`, full tag replacement) were unreachable via MCP at all.

Fix

  • `rivet-core/src/mutate.rs` — new `RESERVED_TOP_LEVEL_KEYS` const (id, type, title, description, status, tags, links, fields, provenance, source-file). `validate_modify` rejects reserved keys in `set_fields` with a targeted hint. `ModifyParams` gains `set_description: Option`.
  • `rivet-core/src/yaml_edit.rs` — new helpers for YAML-safe scalar quoting: `yaml_quote_inline_scalar`, `yaml_double_quote`, `yaml_render_scalar_value`. Double-quoted for single-line YAML-significant chars, block-literal `|-` for multi-line with proper continuation indent. `set_field` splits rendered output on `\n` for block-literal continuation. `set_title`/`set_status` also quote safely now.
  • `rivet-cli/src/mcp.rs` — `ModifyParams` gains `description: Option` sibling of `status`/`title`. No new tool; existing `rivet_modify` surface just got larger.

Test plan

  • 4 MCP stdio integration tests (`rivet-cli/tests/mcp_integration.rs`)
  • 1 mutate integration test (`rivet-core/tests/mutate_integration.rs`)
  • 4 yaml_edit unit tests
  • All green: 22/22 + 20/20 + 29/29
  • CI green

Depends-on: #157 for clean main.rs merge.

🤖 Generated with Claude Code

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Rivet Criterion Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.

Benchmark suite Current: e27c388 Previous: 2fafe1a Ratio
store_insert/100 80852 ns/iter (± 941) 63420 ns/iter (± 225) 1.27
store_insert/1000 843839 ns/iter (± 7447) 678487 ns/iter (± 6153) 1.24
store_insert/10000 14246431 ns/iter (± 928250) 10919758 ns/iter (± 667589) 1.30
store_lookup/100 2108 ns/iter (± 14) 1482 ns/iter (± 2) 1.42
store_lookup/1000 26832 ns/iter (± 129) 18377 ns/iter (± 35) 1.46
store_lookup/10000 371642 ns/iter (± 4805) 269456 ns/iter (± 1380) 1.38
store_by_type/100 95 ns/iter (± 0) 75 ns/iter (± 0) 1.27
store_by_type/1000 95 ns/iter (± 1) 75 ns/iter (± 0) 1.27
store_by_type/10000 96 ns/iter (± 0) 75 ns/iter (± 0) 1.28
schema_load_and_merge 994937 ns/iter (± 19222) 778867 ns/iter (± 8653) 1.28
link_graph_build/100 165306 ns/iter (± 6419) 127731 ns/iter (± 591) 1.29
link_graph_build/1000 1940916 ns/iter (± 166295) 1487602 ns/iter (± 11779) 1.30
link_graph_build/10000 30886677 ns/iter (± 1725918) 23876175 ns/iter (± 1828586) 1.29
validate/100 113263 ns/iter (± 1151) 83361 ns/iter (± 401) 1.36
validate/1000 962329 ns/iter (± 3329) 736705 ns/iter (± 4109) 1.31
traceability_matrix/100 4247 ns/iter (± 18) 3283 ns/iter (± 20) 1.29
traceability_matrix/1000 60209 ns/iter (± 332) 34852 ns/iter (± 112) 1.73
traceability_matrix/10000 769677 ns/iter (± 2499) 564717 ns/iter (± 4408) 1.36
diff/100 62809 ns/iter (± 634) 45470 ns/iter (± 175) 1.38
diff/1000 682508 ns/iter (± 11172) 500593 ns/iter (± 3865) 1.36
diff/10000 7597362 ns/iter (± 241212) 6107115 ns/iter (± 365890) 1.24
query/100 808 ns/iter (± 2) 564 ns/iter (± 13) 1.43
query/1000 7418 ns/iter (± 62) 5187 ns/iter (± 6) 1.43
query/10000 110965 ns/iter (± 1645) 69446 ns/iter (± 4127) 1.60
document_parse/10 23575 ns/iter (± 177) 16784 ns/iter (± 24) 1.40
document_parse/100 165294 ns/iter (± 1234) 114429 ns/iter (± 307) 1.44
document_parse/1000 1523992 ns/iter (± 7257) 1056989 ns/iter (± 13193) 1.44

This comment was automatically generated by workflow using github-action-benchmark.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

❌ Patch coverage is 83.81503% with 28 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rivet-core/src/yaml_edit.rs 82.50% 28 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe avrabe force-pushed the fix/mcp-set-fields-scope branch 2 times, most recently from 00e997b to c2e56f7 Compare April 21, 2026 19:47
…tion` setter

Two related MCP correctness bugs in `rivet_modify`:

1. `set_fields` silently wrote reserved top-level keys (description, title,
   status, ...) under the `fields:` sub-map — corrupting the artifact's
   shape. Values containing backticks or newlines additionally broke the
   YAML emitter, which used unquoted `format!("{key}: {value}")` lines.

2. There was no way to set the top-level `description` (or other top-level
   metadata) via MCP — `set_fields` was the only "generic setter" and, by
   design, targets only the domain `fields:` map.

Design: keep `set_fields` scoped to the `fields:` sub-map and expose
dedicated setters for top-level metadata. `validate_modify` now rejects
any `set_fields` key listed in `RESERVED_TOP_LEVEL_KEYS` (id, type, title,
description, status, tags, links, fields, provenance, source-file) with a
hint pointing at the right parameter. A new `description` parameter on
`rivet_modify` routes through `ModifyParams::set_description`, which
emits YAML-safe scalars — multi-line values become block-literal (`|-`)
scalars, single-line values with YAML-significant characters are
double-quoted with proper escapes. `set_field` in the editor was extended
to splice multi-line values into the line buffer so block scalars stay
well-formed.

Tests (added failing first, now green):
- `test_set_fields_rejects_reserved_description` / `_all_reserved_top_level_keys`
- `test_modify_sets_top_level_description`
- `test_modify_description_with_backticks_and_newlines`
- `test_validate_modify_rejects_reserved_top_level_in_set_fields`
- `test_yaml_quote_inline_scalar_*`, `test_set_field_writes_*_as_*_scalar`

Fixes: REQ-002
@avrabe avrabe force-pushed the fix/mcp-set-fields-scope branch from c2e56f7 to e27c388 Compare April 22, 2026 04:05
@avrabe avrabe merged commit 89d3f43 into main Apr 22, 2026
18 of 22 checks passed
@avrabe avrabe deleted the fix/mcp-set-fields-scope branch April 22, 2026 04:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant